package java_thinking.unit_14._14_7_Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Description :
 *                  通过调用静态方法Proxy.new ProxyInstance()可以创建动态代理，这个方法需要得到一个类加载器(你通常
 *                  可以从已经被加载的对象中获取其类加载器，然后传递给它)，接着是你希望该代理实现的接口列表(不是类或
 *                  抽象类)，以及InvocationHandler接口的一个实现。动态代理可以将所有调用重定向到调用处理器invoke，
 *                  因此通常会调用调用处理器的构造器传递给一个 "实际”对象的引用，从而使得调用处理器在执行其中的任务
 *                  时可以将请求转发
 *
 *                  Proxy 源码大致功能：
 *                  getProxyClass() :返回代理类的 java.lang.Class 对象，并向其提供类加载器和接口数组。
 *                                  实现动态代理的核心方法，动态代理的思路便是生成一个新类，getProxyClass0便成为了生成新类
 *                                  1.先抽取被代理类实现的接口Interface，检测是否符合要求
 *                                  2.查看代理对象缓存中是否有我们要创建的代理类，如果有，直接获取；没有则创建
*                                   getProxyClass0方法里其实直接取proxyClassCache缓存，proxyClassCache是一个代理类的缓存变量
 *                                  ，如果这个缓存里有这个代理类，就直接返回代理类，如果没有，就会通过ProxyClassFactory创建代
 *                                  理对象。
 *                                  ProxyClassFactory 是Proxy里的一个内部类，apply()方法:用来生成代理类(类及类名等)，
 *                                  对代理接口的访问权限整理(public、非public) 最后调用
 *                                  ProxyGenerator.generateProxyClass()方法来完成生成字节码的($Proxy0.class)，根据二进制文件生成相应的Class实例
 *                                  操作见java_thinking.unit_14._14_7_Proxy.Test
 *
 *                  newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
 *                      :返回一个指定接口的代理类实例，该接口可以将方法调用指派到指定的调用处理程序。
 *                      从生成对象方法中，我们看到三个关键的地方：
 *                      Class<?> cl = getProxyClass0(loader, interfaces);//得到代理类
 *                      final Constructor<?> cons = cl.getConstructor(constructorParams);   
 *                      newInstance(cons, ih);//将InvocationHandler h传入代理对象中
                        其实主要一步就在于生成类的过程,类文件中建立被代理对象接口的invoke调用，直接用的Method的反射机制实现的代理；
                        根据生成的class通过反射获取构造函数对象并生成代理类实例
                        见：https://www.cnblogs.com/liuyun1995/p/8157098.html

                    getInvocationHandler(Object proxy)： 返回指定代理实例的调用处理程序。




 *
 * @author: cww
 * @DateTime: 2019-03-31 11:14
 */

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;
    public DynamicProxyHandler(Object proxied){
        this.proxied = proxied;
    }

    /**
     * invoke()内部，在代理上调用方法时需要注意，因为对接口的调用将被重定向为对代理的调用
     * 通常汇之星被代理的操作，然后使用Method.invoke()将请求转发给被代理对象，并传入必须的参数，
     * 这初看起来可能有些受限，就像你只能执行泛化操作一样，但是可以通过传递其他的参数，
     * 来过滤某些方法的调用
     * @param proxy     很少用
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        System.out.println("proxy: "+proxy)
        System.out.println("***\nproxy: "+proxy.getClass().getSimpleName()+",\nmethod: "+method+",\nargs: "+args);
        if (args != null){
            for (Object arg:args){
                System.out.println(" "+arg);
            }
        }
        //方法反射
        return method.invoke(proxied,args);
    }
}
class SimpleDynamicProxy {
    public static void consumer(Interface iface){
        /**
         * 对接口的调用，变成了对代理DynamicProxyHandler的调用
         */
        iface.doSomething();
        iface.somethingElse("bo");
        iface.somethingElse1("bo");
    }

    public static void main(String[] args) {
        RealObject real = new RealObject();
        consumer(real);
        ElseObject elseObject = new ElseObject();
        //这一步取得了elseObject对象的代理，接下来想要调用其方法与使用普通对象无异
        Interface proxy = (Interface)Proxy.newProxyInstance(Interface.class.getClassLoader()
                ,new Class[]{Interface.class},new DynamicProxyHandler(elseObject));
        consumer(proxy);
    }
}
